package com.heima.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.heima.feign.ArticleFeignClient;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.search.dtos.UserSearchDto;
import com.heima.model.search.pojos.ApAssociateWords;
import com.heima.model.search.pojos.ApUserSearch;
import com.heima.model.search.vos.SearchArticleVo;
import com.heima.service.SearchArticleService;
import org.bson.types.ObjectId;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author chenshan
 */
@Service
public class SearchArticleServiceImpl implements SearchArticleService {
    @Autowired
    private RestHighLevelClient client;

    @Autowired
    private ArticleFeignClient articleFeignClient;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Override
    public ResponseResult searchArticle(UserSearchDto searchDto, String id) throws IOException {
        // 校验参数的合法性
        if (!ObjectUtil.isAllNotEmpty(searchDto, searchDto.getSearchWords())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }

        // 校验分页参数并设置默认值
        if (ObjectUtil.isEmpty(searchDto.getPageSize()) || searchDto.getPageSize() <= 0) {
            searchDto.setPageSize(10);
        }

        // 根据搜索关键字匹配查询 title content 按发布时间分页降序查询 并设置搜索关键字高亮
        SearchRequest request = new SearchRequest("article_index");
        request.source().query(QueryBuilders
                .queryStringQuery(searchDto.getSearchWords())
                .field("title")
                .field("content").defaultOperator(Operator.OR))
                .sort("publishTime", SortOrder.DESC)
                .size(searchDto.getPageSize())
                .highlighter(new HighlightBuilder()
                        .field("title")
                        .field("content")
                        .preTags("<font style='color:red; font-size: inherit;'>")
                        .postTags("</font>")
                );

        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        // 获取高亮后的数据
        SearchHits responseHits = response.getHits();

        ArrayList<Map> maps = new ArrayList<>();
        for (SearchHit hit : responseHits) {
            // 得到查询结果
            String source = hit.getSourceAsString();
            Map map = JSONUtil.toBean(source, Map.class);

            // 获取高亮内容
            if (ObjectUtil.isNotEmpty(hit.getHighlightFields())) {
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();

                // 获取高亮标题
                if (ObjectUtil.isNotEmpty(highlightFields.get("title"))) {
                    HighlightField highlightField = highlightFields.get("title");
                    String title = highlightField.getFragments()[0].toString();
                    map.put("h_title", title);
                }

                // 替换查询结果为高亮结果
                maps.add(map);
            }
        }

        // 一步保存搜索关键词到MongoDB中


        return ResponseResult.okResult(maps);
    }

    @Override
    public ResponseResult saveArticle(SearchArticleVo searchArticleVo) throws IOException {
        IndexRequest request = new IndexRequest("article_index");
        request.id(String.valueOf(searchArticleVo.getId()));
        request.source(JSONUtil.toJsonStr(searchArticleVo), XContentType.JSON);

        client.index(request, RequestOptions.DEFAULT);
        return ResponseResult.okResult(null);
    }

    @Override
    public ResponseResult loadSearchHistory(String id) {
        List<ApUserSearch> searchList = mongoTemplate.find(Query.query(Criteria.where("userId")
                        .is(Convert.toInt(id)))
                        .with(PageRequest.of(0, 5))
                        .with(Sort.by("createdTime").descending()),
                ApUserSearch.class, "ap_user_search");
        return ResponseResult.okResult(searchList);

    }

    @Override
    public ResponseResult delSearchHistory(String id) {
        mongoTemplate.remove(Query.query(Criteria.where("_id").is(new ObjectId(id))), "ap_user_search");
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    @Override
    public ResponseResult searchAssociate(UserSearchDto searchDto) {
        List<ApAssociateWords> wordsList = mongoTemplate.find(Query.query(Criteria.where("associateWords").regex(searchDto.getSearchWords()))
                        .with(PageRequest.of(0, 5))
                        .with(Sort.by("createdTime").descending()),
                ApAssociateWords.class, "ap_associate_words");
        return ResponseResult.okResult(wordsList);

    }

    /**
     * 一步保存搜索记录
     */
    @Async
    public void saveKeyWordsToMongoDB(String id, String keyWords) {
        // 查询数据
        List<ApUserSearch> userSearchList = mongoTemplate.find(Query.query(Criteria.where("userId").is(Convert.toInt(id))).with(Sort.by("createdTime").ascending()), ApUserSearch.class);
        for (ApUserSearch userSearch : userSearchList) {
            if (userSearch.getKeyword().equalsIgnoreCase(keyWords)) {
                // 更新关键词时间
                userSearch.setCreatedTime(new Date());
                mongoTemplate.update(userSearch.getClass());
                return;
            }
        }

        // 关键词不存在且MongoDB中数据超过10条
        if (CollUtil.isNotEmpty(userSearchList) && userSearchList.size() >= 10) {
            ApUserSearch userSearch = userSearchList.get(0);
            mongoTemplate.remove(userSearch.getClass());
        }

        ApUserSearch userSearch = new ApUserSearch();
        userSearch.setId(String.valueOf(new ObjectId(id)));
        userSearch.setKeyword(keyWords);
        userSearch.setCreatedTime(new Date());
        userSearch.setUserId(Convert.toInt(id));

        mongoTemplate.insert(userSearch, "ap_user_search");
    }
}
